package com.hrpc;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.atomic.LongAdder;

public class IdGenerate {
    //雪花算法
    //1.机房号(数据中心) 5bit
    //2.机器号 5bit
    //3.时间戳(long 1970年0时0分到现在的毫秒数)，必须减少到（32位）自由选择一个比较近的时间，比如公司成立的时间
    //4.同一个机房的同一个机器号的同一个时间可能因为并发量很大需要多个ID--->序列号 12bit
//    5+5+12+42=64
    //起始时间戳
    public static final long START_STAMP=DateUtil.get("2022-1-1").getTime();
    //机房号
    public static final long DATA_CENTER_BIT=5L;
    //机器号
    public static final long MACHINE_BIT=5L;
    //序列号
    public static final long SEQUENCE_BIT=12L;
    //最大值 Math.pow(2,5)-1    ~表示取反
    public static final long DATA_CENTER_MAX=~(-1L<<DATA_CENTER_BIT);
    public static final long MACHINE_BIT_MAX=~(-1L<<MACHINE_BIT);
    public static final long SEQUENCE_MAX=~(-1L<<SEQUENCE_BIT);

    //1.整体组成部分：时间戳（42）+机房号（5）+机器号（5）+序列化（12）
    //表示时间戳要左移多少位
    public static final long TIME_STAMP_LEFT=22L;
    public static final long DATA_LEFT=17L;
    public static final long MACHINE_LEFT=12L;

    private long dataCenterID;
    private long machineID;
    //LongAdder是线程安全的累加器
    private LongAdder sequenceID=new LongAdder();
    //TODO 时钟回拨的问题需要处理
    private long last_timestamp=-1L;

    //判断传入的参数是否合法
    public IdGenerate(long dataCenterID, long machineID) {
        if (dataCenterID > DATA_CENTER_MAX ||machineID>MACHINE_BIT_MAX){
            throw new RuntimeException("传入的数据中心编号或机器号不合法！！！");
        }
        this.dataCenterID = dataCenterID;
        this.machineID = machineID;
    }

    public long getID(){
        //1.处理时间戳
        long currentTime=System.currentTimeMillis();
        long timeStamp=currentTime - START_STAMP;
        //判断时钟回拨
        if (timeStamp < last_timestamp){
            throw new RuntimeException("发生了时钟回拨！！！");
        }
        //需要做一些处理对sequenceID，如果是同一个节点，必须要做自增
        if (timeStamp == last_timestamp){
            sequenceID.increment();
            if (sequenceID.sum() >= SEQUENCE_MAX){
                //停顿一下等下一个时间戳，拿到之后将sequenceID置0
                timeStamp=getNextTimeStamp();
                sequenceID.reset();
            }
        }else {
            sequenceID.reset();
        }
        //执行结束将时间戳赋值给last_timestamp
        last_timestamp = timeStamp;
        long sequence=sequenceID.sum();
        return timeStamp<<TIME_STAMP_LEFT | dataCenterID<<DATA_LEFT | machineID<<MACHINE_LEFT | sequence;
    }

    private long getNextTimeStamp() {
        //先获取当前时间戳
        long current = System.currentTimeMillis() - START_STAMP;
        //判断当前时间戳和之前时间戳是不是一样，如果一样那么在循环里，不一样再返回
        while (current == last_timestamp){
            current = System.currentTimeMillis() - START_STAMP;
        }
        return current;
    }

    public static void main(String[] args) {
        IdGenerate idGenerate=new IdGenerate(1,2);
        for (int i=0;i<1000;i++){
            new Thread(()->{
                System.out.println(idGenerate.getID());
            }).start();
        }
    }
}
